package com.db.collections;

public class IntegerHashMapImpl implements IntegerHashMap {

	private static final float DEFAULT_LOAD_FACTOR = 0.75f;
	private static final int DEFAULT_INITIAL_CAPACITY = 16;

	static class Entry {
		final int key;
		int value;
		Entry next;

		Entry(int k, int v, Entry n) {
			key = k;
			value = v;
			next = n;
		}
	}

	private Entry[] table;
	private float loadFactor;
	private int threshold;
	private int size;

	public IntegerHashMapImpl() {
		table = new Entry[DEFAULT_INITIAL_CAPACITY];
		loadFactor = DEFAULT_LOAD_FACTOR;
		threshold = (int) (table.length * loadFactor);
	}

	@Override
	public void put(int key, int value) {
		int bucketIndex = indexFor(key);
		for (Entry e = table[bucketIndex]; e != null; e = e.next) {
			if (key == e.key) {
				e.value = value;
				return;
			}
		}

		addEntry(key, value, bucketIndex);
	}

	private void addEntry(int key, int value, int bucketIndex) {
		if ((size >= threshold) && (table[bucketIndex] != null)) {
			resize();
			bucketIndex = indexFor(key);
		}

		Entry e = table[bucketIndex];
		table[bucketIndex] = new Entry(key, value, e);
		size++;
	}

	private void resize() {
		Entry[] oldTable = table;

		table = new Entry[oldTable.length * 2];

		for (Entry e : oldTable) {
			while (null != e) {
				Entry next = e.next;
				int bucketIndex = indexFor(e.key);
				e.next = table[bucketIndex];
				table[bucketIndex] = e;
				e = next;
			}
		}
		threshold = (int) (table.length * loadFactor);
	}

	private int indexFor(int key) {
		int h = key;
		// h ^= (h >>> 20) ^ (h >>> 12);
		// h = h ^ (h >>> 7) ^ (h >>> 4);
		return h & (table.length - 1);
	}

	@Override
	public void remove(int key) {
		int bucketIndex = indexFor(key);
		Entry prev = table[bucketIndex];
		Entry e = prev;

		while (e != null) {
			Entry next = e.next;
			if (key == e.key) {
				size--;
				if (prev == e)
					table[bucketIndex] = next;
				else
					prev.next = next;
				return;
			}
			prev = e;
			e = next;
		}
	}

	@Override
	public int get(int key) {
		Entry e = getEntryForKey(key);
		return e == null ? 0 : e.value;
	}

	@Override
	public boolean contains(int key) {
		Entry e = getEntryForKey(key);
		return e == null ? false : true;
	}

	private Entry getEntryForKey(int key) {
		int bucketIndex = indexFor(key);
		for (Entry e = table[bucketIndex]; e != null; e = e.next) {
			if (key == e.key) {
				return e;
			}
		}

		return null;
	}

}
